package services

import (
	"errors"
	"fmt"
	"time"

	"game-simple/internal/framework/auth/dto"
	"game-simple/internal/framework/auth/models"
	"game-simple/internal/infrastructure/config"
	"game-simple/internal/infrastructure/core"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

type Service struct {
	jwtService *JWTService
}

func NewService() *Service {
	return &Service{
		jwtService: NewJWTService(),
	}
}

// Login 用户登录
func (s *Service) Login(req *dto.LoginRequest) (*dto.AuthResponse, error) {
	infra := core.Get()

	var userModel models.UserModel
	if err := infra.DB.Where("username = ?", req.Username).First(&userModel).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("用户名或密码错误")
		}
		return nil, fmt.Errorf("查询用户失败: %w", err)
	}

	// 验证密码
	if err := bcrypt.CompareHashAndPassword([]byte(userModel.Password), []byte(req.Password)); err != nil {
		return nil, errors.New("用户名或密码错误")
	}

	// 生成 JWT token
	token, err := s.jwtService.GenerateToken(userModel.ID, userModel.Username)
	if err != nil {
		return nil, fmt.Errorf("生成 token 失败: %w", err)
	}

	cfg := config.GetConfig()
	expiresAt := time.Duration(cfg.JWT.ExpiresHours) * time.Hour

	// 将 JWT token 存入 Redis
	tokenKey := fmt.Sprintf("auth:jwt:%s", token)
	sessionData := fmt.Sprintf("%d:%s", userModel.ID, userModel.Username)
	if err := infra.Cache.Set(tokenKey, sessionData, expiresAt); err != nil {
		return nil, fmt.Errorf("保存 token 到 Redis 失败: %w", err)
	}

	// 同时存储用户 ID 到 token 的映射
	userTokenKey := fmt.Sprintf("auth:user:%d", userModel.ID)
	if err := infra.Cache.Set(userTokenKey, token, expiresAt); err != nil {
		return nil, fmt.Errorf("保存用户 token 映射失败: %w", err)
	}

	// 转换为响应
	userResp := userModel.ToUserResponse()

	return &dto.AuthResponse{
		Token: token,
		User:  userResp,
	}, nil
}

// Logout 用户登出
func (s *Service) Logout(tokenString string, userID uint) error {
	infra := core.Get()

	// 从 Redis 中删除 JWT token
	tokenKey := fmt.Sprintf("auth:jwt:%s", tokenString)
	if err := infra.Cache.Del(tokenKey); err != nil {
		return fmt.Errorf("删除 token 失败: %w", err)
	}

	// 删除用户 token 映射
	userTokenKey := fmt.Sprintf("auth:user:%d", userID)
	if err := infra.Cache.Del(userTokenKey); err != nil {
		return fmt.Errorf("删除用户 token 映射失败: %w", err)
	}

	return nil
}

// ValidateToken 验证 token 并返回用户信息
func (s *Service) ValidateToken(tokenString string) (*models.UserModel, error) {
	infra := core.Get()

	// 首先检查 Redis 中是否存在该 token
	tokenKey := fmt.Sprintf("auth:jwt:%s", tokenString)
	exists, err := infra.Cache.Exists(tokenKey)
	if err != nil {
		return nil, fmt.Errorf("检查 Redis token 失败: %w", err)
	}
	if !exists {
		return nil, errors.New("token 不存在或已过期")
	}

	// 验证 JWT token 格式和签名
	claims, err := s.jwtService.ValidateToken(tokenString)
	if err != nil {
		return nil, fmt.Errorf("无效的 token: %w", err)
	}

	// 从 Redis 获取会话数据验证一致性
	sessionData, err := infra.Cache.Get(tokenKey)
	if err != nil {
		return nil, fmt.Errorf("获取会话数据失败: %w", err)
	}

	// 验证 Redis 中的用户 ID 与 JWT claims 中的用户 ID 一致
	expectedData := fmt.Sprintf("%d:%s", claims.UserID, claims.Username)
	if sessionData != expectedData {
		return nil, errors.New("token 数据不一致")
	}

	// 查询用户信息
	var userModel models.UserModel
	if err := infra.DB.First(&userModel, claims.UserID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("用户不存在")
		}
		return nil, fmt.Errorf("查询用户失败: %w", err)
	}

	return &userModel, nil
}

// RefreshToken 刷新 token
func (s *Service) RefreshToken(tokenString string) (*dto.AuthResponse, error) {
	// 验证旧 token（包括 Redis 检查）
	_, err := s.ValidateToken(tokenString)
	if err != nil {
		return nil, fmt.Errorf("验证旧 token 失败: %w", err)
	}

	// 解析旧 token 获取用户信息
	claims, err := s.jwtService.ValidateToken(tokenString)
	if err != nil {
		return nil, fmt.Errorf("解析旧 token 失败: %w", err)
	}

	// 生成新 token
	newToken, err := s.jwtService.GenerateToken(claims.UserID, claims.Username)
	if err != nil {
		return nil, fmt.Errorf("生成新 token 失败: %w", err)
	}

	infra := core.Get()
	cfg := config.GetConfig()
	expiresAt := time.Duration(cfg.JWT.ExpiresHours) * time.Hour

	// 删除旧 token
	oldTokenKey := fmt.Sprintf("auth:jwt:%s", tokenString)
	if err := infra.Cache.Del(oldTokenKey); err != nil {
		return nil, fmt.Errorf("删除旧 token 失败: %w", err)
	}

	// 存储新 token 到 Redis
	newTokenKey := fmt.Sprintf("auth:jwt:%s", newToken)
	sessionData := fmt.Sprintf("%d:%s", claims.UserID, claims.Username)
	if err := infra.Cache.Set(newTokenKey, sessionData, expiresAt); err != nil {
		return nil, fmt.Errorf("保存新 token 失败: %w", err)
	}

	// 更新用户 token 映射
	userTokenKey := fmt.Sprintf("auth:user:%d", claims.UserID)
	if err := infra.Cache.Set(userTokenKey, newToken, expiresAt); err != nil {
		return nil, fmt.Errorf("更新用户 token 映射失败: %w", err)
	}

	// 查询用户信息
	var userModel models.UserModel
	if err := infra.DB.First(&userModel, claims.UserID).Error; err != nil {
		return nil, fmt.Errorf("查询用户失败: %w", err)
	}

	userResp := userModel.ToUserResponse()
	return &dto.AuthResponse{
		Token: newToken,
		User:  userResp,
	}, nil
}

